var path = require('path');
var appPath = require('electron-root-path').rootPath;

var db = require(path.resolve(appPath, './lib/db'));
var async = require('async');
var User = require(path.resolve(appPath, './lib/user'));
// var db.notebooks = db.notebooks;
var Common = require(path.resolve(appPath, './lib/common'));
var Web = require(path.resolve(appPath, './lib/web'));

function log(o) {console.log(o);}

/*
字段: 
_id, // NotebookId表示是服务端Id, _id表示本地的id, 不一样
IsDeleted bool, // 是否已删除, true表示本地已删除, 未同步
LocalUsn int 本地Usn, 递增, 用于记录从上次同步后是否已修改, 若 > lastUpdateCount则表示已修改
	NotebookId       bson.ObjectId `bson:"_id,omitempty"` // 必须要设置bson:"_id" 不然mgo不会认为是主键
	UserId           bson.ObjectId `bson:"UserId"`
	ParentNotebookId bson.ObjectId `bson:"ParentNotebookId,omitempty"` // 上级
	Seq              int           `Seq`                               // 排序
	Title            string        `Title`                             // 标题
	UrlTitle         string        `UrlTitle`                          // Url标题 2014/11.11加
	NumberNotes      int           `NumberNotes`                       // 笔记数
	IsTrash          bool          `IsTrash,omitempty`                 // 是否是trash, 默认是false
	IsBlog           bool          `IsBlog,omitempty`                  // 是否是Blog 2013/12/29 新加
	CreatedTime      time.Time     `CreatedTime,omitempty`
	UpdatedTime      time.Time     `UpdatedTime,omitempty`

	// 2015/1/15, 更新序号
	Usn int `Usn` // UpdateSequenceNum, 与服务器端保存一致
 
 */

// 排序笔记本和子笔记本
function sortNotebooks(notebooks) {
	notebooks.sort(function(a, b) {
		return a.Seq - b.Seq;
	});
	for(var i in notebooks) {
		var notebook = notebooks[i];
		if(notebook.Subs.length > 0) {
			sortNotebooks(notebook.Subs);
		}
	}
}

// 笔记本服务
var Notebook = {

	addNotebookTest: function() {
		var doc = {
			// _id: "xxxxx",
			hello: 'world'
		   , n: 5
		   , today: new Date()
		   , nedbIsAwesome: true
		   , notthere: null
		   , notToBeSaved: undefined  // Will not be saved
		   , fruits: [ 'apple', 'orange', 'pear' ]
		   , infos: { name: 'nedb' }
		   };
		console.log("save before")
		db.notebooks.insert(doc, function (err, newDoc) {   // Callback is optional
		  // newDoc is the newly inserted document, including its _id
		  // newDoc has no key called notToBeSaved since its value was undefined
		  // console.log(err);
		  // console.log(newDoc);
		});
	},

	// 建立关联
	_mapNotebooks: function(notebooks) {
		var me = this;
		// log(notebooks);
		// 整理成层级关系, 并排序
		// 1. 建立map
		var notebooksMap = {};
		for(var i in notebooks) {
			var notebook = notebooks[i];
			notebook.Subs = [];
			notebooksMap[notebook.NotebookId] = notebook;
		}
		// 2. 追加到父下
		var isolatedNotebooks = []; // 独立的, 没有父的, 第一级
		for(var notebookId in notebooksMap) {
			var notebook = notebooksMap[notebookId];
			var parentNotebookId = notebook.ParentNotebookId;
			if(parentNotebookId && notebooksMap[parentNotebookId]) {
				notebooksMap[parentNotebookId].Subs.push(notebook);
			} else {
				isolatedNotebooks.push(notebook);
			}
		}
		// 3. 排序
		sortNotebooks(isolatedNotebooks);
		// log(notebooks);
		// log(notebooksMap['1'].Subs);
		return isolatedNotebooks;
	},

	// 得到用户下所有的notebook
	// 排序好之后返回
	getNotebooks: function(callback) {
		var me = this;
		var userId = User.getCurActiveUserId();
		db.notebooks.find({UserId: userId, $or: [{LocalIsDelete : { $exists : false }}, {LocalIsDelete: false}] }, function(err, notebooks) {
			if(err) {
				log(err);
				return callback && callback(false);
			}
			
			callback && callback(me._mapNotebooks(notebooks));
		});
	},

	// 新建笔记本
	// 这里, 之前有个大BUG, pull过来后添加到tree上设为IsNew, 导致添加大量重复的notebook
	addNotebook: function(notebookId, title, parentNotebookId, callback) {
		var notebook = {
			Title: title,
			Seq: -1,
			UserId: User.getCurActiveUserId(),
			ParentNotebookId: parentNotebookId,
			LocalIsNew: true,
			IsDirty: true, // 必须, 同步后才为非dirty状态
			// TODO UrlTitle
		}
		if(notebookId) {
			notebook['NotebookId'] = notebookId;
		}
		db.notebooks.insert(notebook, function (err, newDoc) {   // Callback is optional
			if(err) {
				console.log(err);
				callback && callback(false);
			} else {
				callback && callback(newDoc);
			}
		});
	},

	// 修改笔记本标题
	updateNotebookTitle: function(notebookId, title, callback) {
		db.notebooks.update({NotebookId: notebookId}, 
			{$set: 
				{Title: title, IsDirty: true, UpdatedTime: new Date()}
			}, function(err, n) {
			callback(true);
		});
	},

	// 拖动笔记本
	// 当前笔记在parentNotebookId下, 且该parentNotebookId下的所有子孩子的id
	dragNotebooks: function(curNotebookId, parentNotebookId, siblingNotebookIds) {
		var me = this;
		console.log(curNotebookId);
		console.log(parentNotebookId);
		console.log(siblingNotebookIds);
		// 先更新curNotebookId的父
		me.getNotebook(curNotebookId, function(notebook) {
			if(!notebook) {
				console.log('not fount');
				return;
			}
			// 先更新之
			// db.notebooks.update({NotebookId: notebookId}, {$set: {ParentNotebookId: parentNotebookId, IsDirty: true, UpdatedTime: new Date()}}, function(err, n) {
			// });
			siblingNotebookIds = siblingNotebookIds || [];
			// 再更新所有子孩子的seq
			for(var i = 0; i < siblingNotebookIds.length; ++i) {
				var siblingNotebookId = siblingNotebookIds[i];
				console.log('siblingNotebookId: ' + siblingNotebookId);
				db.notebooks.update({NotebookId: siblingNotebookId}, 
					{$set: 
						{ParentNotebookId: parentNotebookId, Seq: i, IsDirty: true, UpdatedTime: new Date()}
					}
				);
			}
		});
	},

	// 修改笔记本
	updateNotebook: function(notebookId, callback) {

	},

	// 删除笔记本
	deleteNotebook: function(notebookId, callback) {
		// 先检查是否有笔记, 如果有, 则不准删除
		var Note = require(path.resolve(appPath, './lib/note'));
		Note.hasNotes(notebookId, function(has) {
			if(has) {
				callback(false, 'This notebook has notes, please delete notes firstly.');
			} else {
				db.notebooks.update({NotebookId: notebookId}, {$set: {LocalIsDelete: true, IsDirty: true, UpdatedTime: new Date()}}, function(err, n) {
					callback(true);
				});
			}
		});

		/*
		db.notebooks.remove({NotebookId: notebookId}, function(err, n) {
			callback();
		});
		*/
	},

	// 删除本地的笔记本, 是New又是Delete
	deleteLocalNotebook: function(notebookId, callback) {
		db.notebooks.remove({NotebookId: notebookId}, function(err, n) {
			callback && callback();
		});
	},

	// addNote, 删除note, 移动note
	// 重新统计笔记本的笔记数据
	reCountNotebookNumberNotes: function(notebookId) {
		db.notes.count({NotebookId: notebookId,
				// 现在还不明确为什么会有IsDeleted的笔记
				$or:[
					{IsDeleted: {$exists: false}},
					{IsDeleted: false}
				],
				IsTrash: false,
				LocalIsDelete: false
			}, function(err, count) {
			if(err) {
				log(err);
				return;
			}
			Web.updateNotebookNumberNotes(notebookId, count);
			db.notebooks.update({NotebookId: notebookId}, {$set: {NumberNotes: count}}, {})
		});
	},

	// 得到笔记本
	getNotebook: function(notebookId, callback) {
		var me = this;
		db.notebooks.findOne({NotebookId: notebookId}, function(err, doc) {
			if(err || !doc) {
				log('不存在');
				callback && callback(false);
			} else {
				callback && callback(doc);
			}
		});
	},


	//----------------
	// 同步 
	//----------------
	
	getNotebookByServerNotebookId: function(notebookId, callback) {
		var me = this;
		db.notebooks.findOne({ServerNotebookId: notebookId}, function(err, doc) {
			if(err || !doc) {
				log('不存在');
				callback && callback(false);
			} else {
				callback && callback(doc);
			}
		});
	},

	// 通过ServerNotebookId得到NotebookId
	getNotebookIdByServerNotebookId: function(serverNotebookId, callback) {
		if(!serverNotebookId) {
			return callback(false);
		}
		db.notebooks.findOne({ServerNotebookId: serverNotebookId}, function(err, notebook) {
			if(err || !notebook) {
				return callback(false);
			}
			callback(notebook.NotebookId);
		});
	},
	// 发送changes时用 api调用
	getServerNotebookIdByNotebookId: function(notebookId, callback) {
		if(!notebookId) {
			return callback(false);
		}
		db.notebooks.findOne({NotebookId: notebookId}, function(err, notebook) {
			if(err || !notebook) {
				return callback(false);
			}
			callback(notebook.ServerNotebookId);
		});
	},

	// 强制删除
	deleteNotebookForce: function(notebookId, callback) {
		var me = this;
		db.notebooks.remove({ServerNotebookId: notebookId}, function(err, n) {
			if(err) { 
				callback && callback(false);
			} else {
				callback && callback(true);
			}
		});
	},

	// 添加笔记本, notebook object
	// 这里的notebook是服务器传过来的数据, 需要fix下, 
	addNotebookForce: function(notebook, callback) {
		var me = this;
		notebook.ServerNotebookId = notebook.NotebookId;
		// notebook.NotebookId = Common.objectId();
		notebook.NotebookId = notebook.NotebookId; // 就采用服务器的, 怕失去了层级
		me.getNotebookIdByServerNotebookId(notebook.ParentNotebookId, function(parentNotebookId) {
			// 如果是第一次添加可能会有问题, 数据库还没有数据, 那么还找不到
			if(parentNotebookId) {
				notebook.ParentNotebookId = parentNotebookId;
			} else {
				// 否则, 就用服务器上的
			}

			notebook.CreatedTime = Common.goNowToDate(notebook.CreatedTime);
			notebook.UpdatedTime = Common.goNowToDate(notebook.UpdatedTime);

			notebook.IsDirty = false;
			notebook.LocalIsNew = false;
			notebook.LocalIsDelete = false;

			db.notebooks.insert(notebook, function (err, newDoc) {   // Callback is optional
				if(err) {
					console.log(err);
					callback && callback(false);
				} else {
					callback && callback(newDoc);
				}
			});
		});
	},
	// 更新笔记本
	// 这里的notebook是服务器传过来的数据, 需要fix下, 
	updateNotebookForce: function(notebook, notebookLocal, callback) {
		var me = this;

		notebook.IsDirty = false;
		notebook.LocalIsNew = false;
		notebook.LocalIsDelete = false;

		delete notebook['NumberNotes'];
		delete notebook['UpdatedTime'];
		delete notebook['CreatedTime'];

		var serverNotebookId = notebook.NotebookId;
		me.getNotebookIdByServerNotebookId(notebook.ParentNotebookId, function(parentNotebookId) {
			notebook.ParentNotebookId = parentNotebookId;
			notebook.ServerNotebookId = notebook.NotebookId;
			notebook.NotebookId = notebookLocal.NotebookId;
			db.notebooks.update({ServerNotebookId: serverNotebookId}, {$set: notebook}, {}, function (err, updates) {   // Callback is optional
				if(err) {
					console.log(err);
					callback && callback(false);
				} else {
					callback && callback(notebook);
				}
			});
		});
	},

	//

	// 更新笔记本, NotebookId可能也要更改
	// notebook是服务器传过来的
	updateNotebookForceForSendChange: function(notebookId, notebook, callback) {
		// console.log('updateNotebookForceForSendChange notebook是服务器传过来的');
		var me = this;
		notebook.IsDirty = false;
		notebook.LocalIsNew = false;
		notebook.ServerNotebookId = notebook.NotebookId; // ? 怎么可能要改呢? 因为这可能是添加后的笔记本
		notebook.NotebookId = notebookId; // 必须设为本地, 因为notebook.NotebookId是服务器传过来的 2/16 fixed

		me.getNotebookIdByServerNotebookId(notebook.ParentNotebookId, function(parentNotebookId) {
			notebook.ParentNotebookId = parentNotebookId;
			// console.log(notebook2);
			// notebook2.Title  += " H-";
			// multi, 因为历史原因, 导致大量重复notebookId的元素
			db.notebooks.update({NotebookId: notebookId}, {$set: notebook}, {multi: true}, function (err, n) {
				// console.log('updateNotebookForceForSendChange end' + notebookId + ' ' + n);
				if(err) {
					console.log(err);
					callback && callback(false);
				} else {
					callback && callback(notebook);
				}
			});
		});
	},

	// 深度优先一个列表
	// 为了send changes时避免先send child
	_deepTraversal: [],
	_visited: {}, // 可以不要
	deep: function(T) {
		var me = this;
		if(!T || !T.Subs || T.Subs.length == 0) {
			return;
		}
		for(var i = 0; i < T.Subs.length; ++i) {
			var node = T.Subs[i];
			if(!me._visited[node.NotebookId]) { // 可以不要这个判断
				me._visited[node.NotebookId] = true;
				me._deepTraversal.push(T.Subs[i]);
				// 递归
				me.deep(T.Subs[i]);
			}
		}
	},

	// 获得用户修改的笔记本
	getDirtyNotebooks: function(callback) {
		var me = this;
		db.notebooks.find({UserId: User.getCurActiveUserId(), IsDirty: true}, function(err, notebooks) {
			if(err) {
				log(err);
				return callback && callback(false);
			} else {
				var mapNotebooks = me._mapNotebooks(notebooks);
				// 深度优先一个序列
				me._deepTraversal = [];
				me._visited = {};
				me.deep({Subs: mapNotebooks});
				// 返回之
				callback(me._deepTraversal);
			}
		});
	},

	// 处理冲突
	// notes是服务器的数据, 与本地的有冲突
	// 1) 将本地的note复制一份
	// 2) 服务器替换之前
	fixConflicts: function(notebookSyncInfo, callback) {
		var me = this;

		// 服务器没有, 但是是发送更新的, 所以需要作为添加
		// 情况很少见
		if(notebookSyncInfo.changeNeedAdds) { 
			var needAddNotebooks = notebookSyncInfo.changeNeedAdds;
			for(var i in needAddNotebooks) {
				var notebook = needAddNotebooks[i];
				me.setIsNew(notebook.NotebookId);
			}
		}

		// pull
		// 处理添加的, 更新的, 这里前端统一重新渲染!!
		var adds = notebookSyncInfo.adds;
		if (!isEmpty(adds) || !isEmpty(notebookSyncInfo.updates)) {
			console.log('	has adds/updates notebook', adds, notebookSyncInfo.updates);
			// Web.addSyncNotebook(adds);
			// Web.updateSyncNotebook(notebookSyncInfo.updates);

			Web.reloadNotebook();
		}

		// push
		if (!isEmpty(notebookSyncInfo.changeAdds)) {
			console.log('	has changeAdds notebook', notebookSyncInfo.changeAdds);
			Web.addChangeNotebook(notebookSyncInfo.changeAdds);
		}
		if (!isEmpty(notebookSyncInfo.changeUpdates)) {
			console.log('	has changeUpdates notebook', notebookSyncInfo.changeUpdates);
			Web.updateChangeNotebook(notebookSyncInfo.changeUpdates);
		}

		if (!isEmpty(notebookSyncInfo.deletes)) {
			// 处理删除的
			Web.deleteSyncNotebook(notebookSyncInfo.deletes);
		}

		// 没有冲突的, 因为notebook不处理冲突
		// 处理冲突
		var conflictNotebooks = notebookSyncInfo.conflicts || [];
		conflictNotebooks.length && console.log('	fix notebook conflicts');
		async.eachSeries(conflictNotebooks, function(notebook, cb) { 
			/*
			var noteId = note.NoteId;
			// 复制一份
			me.copyNoteForConfict(noteId, function(newNote) {
				if(newNote) {
					// 更新之前的
					me.updateNotebookForce(note, function() { 
						cb();
						// 前端来处理, 全量sync时不用前端一个个处理
						notebookWeb.fixSyncConflict && notebookWeb.fixSyncConflict(note, newNote);
					});
				} else {
					cb();
				}
			});
			*/
			cb();
		}, function() {
			// 最后调用
			callback && callback();
		});
	},

	// 在send delete笔记时成功
	setNotDirty: function(notebookId) {
		db.notebooks.update({NotebookId: notebookId}, {$set:{IsDirty: false}})
	},
	// 在send delete笔记时有冲突
	setNotDirtyNotDelete: function(notebookId) {
		db.notebooks.update({NotebookId: notebookId}, {$set:{IsDirty: false, LocalIsDelete: false}})
	},
	setIsNew: function(notebookId) {
		db.notebooks.update({NotebookId: notebookId}, {$set:{LocalIsNew: true, IsDirty: true}})
	}
};
module.exports = Notebook;